/*************************************************************************
** Name		: AppLoad.h
** Desc		: 应用模块加载框架。目的：规范化多模块应用的启动流程
** Author	: xktesla
*************************************************************************/

#ifndef _8A2DE3FF_1989_4B70_B9CF_2CCA21D186CC
#define _8A2DE3FF_1989_4B70_B9CF_2CCA21D186CC

#include "xUtils.h"
#include "xCores.h"
#include "ISingleton.h"

namespace x2lib
{
    class AppLoad: public ISingleton<AppLoad>
    {
        friend class ISingleton<AppLoad>;

    public:
        class Event
        {
        public:
            enum
            {
                // 命名规则：Event_Aaaa_Bbbbbbbb_CCC, 
                // 消息名为Bbbbbbbb，该消息由Aaaa模块主管，CCC为REQ代表其他模块向Aaaa发起请求，CCC为REP代表Aaaa向其他模块发送通知
                Event_Core_None = (uint32_t)0,

                // Core消息不管是否有listener，一律添加到消息队列
                Event_Core_Base = (uint32_t)1000,
                Event_Core_Unknown = Event_Core_Base,
                // Event_Core_AppInit_REQ,                          // Post: Master->Module
                // Event_Core_AppInit_REP,                          // Post: Module->Master
                Event_Core_ModInit_REQ,                             // Post: Module->Master
                Event_Core_ModInit_REP,                             // Post: Module->Master
                Event_Core_ModExit_REQ,                             // Post: Module->Master
                Event_Core_ModExit_REP,                             // Post: Module->Master
                Event_Core_AppExit_REQ,                             // Post: Any->Master
                Event_Core_AppExit_REP,                             // Post: Module->Master, 当Master统计到所有Module都发送了Event_CORE_APPEXIT_REP后，真正退出
                // Event_Core_AppExec_REQ,                          // Post: Module->Master
                Event_Core_AppTick_REP,                             // Post: Module->Master
                Event_Core_Dead,                                    // 结束标记

                Event_Core_UserBase = (uint32_t)10000,
            };

            Event(const uint32_t uEvtId = Event_Core_Unknown)
            {
                this->fromId = 0;
                this->isSys = false;
                this->uDbgId = 0;
                this->uEvtId = uEvtId;
                this->isPost = false;
                this->tickId = 0;
                this->iError = 0;
                this->userInt = 0;
                this->userPtr = nullptr;
                memset(userRam, 0, sizeof(userRam));
            }
        
        public:
            uint32_t fromId;
            uint32_t isSys;
            uint32_t uDbgId;
            uint32_t uEvtId;
            uint32_t isPost;
            uint32_t tickId;
            uint32_t iError;
            // 用户自定义数据
            uint32_t userInt;
            void* userPtr;
            char userRam[256];
        };

        class AppModule;
        class AppMaster;
        class Listener
        {
            friend class AppLoad;
            friend class AppModule;
            friend class AppMaster;
        public:
            Listener(AppModule* appModule)
            {
                m_pAppModule = appModule;
            }

        protected:
            virtual void onAppEvent(Event* event) {};

        private:
            AppModule* m_pAppModule;
        };
        
        class AppModule: public Listener, public xCores::Engine
        {
            friend class AppLoad;
            friend class Listener;
            friend class AppMaster;

        public:
            AppModule(AppMaster* appMaster, const std::string& strName) :
                Listener(this),
                appMaster(appMaster), strName(strName), isLoading(true), isReadied(false)
            {
                isSucceed[0] = false;
                isSucceed[1] = false;
                appMaster->regist(this);
            }

            void SetReadied(bool isReadied) { this->isReadied = isReadied; };
            bool GetReadied() { return this->isReadied; }

        protected:
            virtual void onEngineExec(uint32_t id, void* vData) override {};
            virtual void onEngineExit(uint32_t id, void* vData) override {};

            // hook目标queEvent
            virtual bool hookPostEvent(Event* event) { return false; };
            virtual bool hookSendEvent(Event* event) { return false; };
            // onModuleXXXX运行在AppModule线程
            virtual bool onModuleInit() { return true; }; // 运行在Module线程
            virtual int onModuleExec() { while(dispEvent(waitEvent())); return 0; }
            virtual bool onModuleExit() { return true; } // 运行在Module线程
            virtual void onAppReqInit() {}; // 运行在Master线程，此时所有onModuleInit均未开始执行
            virtual void onAppReqExit() {}; // 运行在Master线程，此时所有onModuleExit均未开始执行
            
            Event* waitEvent()
            {
                m_signDeal.Acquire();
                Event* event = m_queEvent.front();
                m_queEvent.pop();
                return event;
            }

            // 如果waitEvent后不调用dispEvent，则需要手动删除event
            bool dispEvent(Event* event)
            {
                //m_mtxListener.Lock();
                auto listeners = m_mapListener.find(event->uEvtId);
                if (listeners != m_mapListener.end())
                {
                    for(auto& it: listeners->second)
                    {
                        it->onAppEvent(event);
                    }
                }
                //m_mtxListener.Unlock();

                if (event->uEvtId == AppLoad::Event::Event_Core_ModInit_REQ || event->uEvtId == AppLoad::Event::Event_Core_ModExit_REQ)
                {
                    appMaster->onModuleReport((AppModule*)event->userPtr, false, false);
                }
                else if(event->uEvtId == AppLoad::Event::Event_Core_ModInit_REP || event->uEvtId == AppLoad::Event::Event_Core_ModExit_REP)
                {
                    AppMaster* appMaster = AppLoad::GetInst()->getMaster();
                    AppModule* appModule = (AppModule*)event->userPtr;
                    if (event->fromId == appMaster->m_threadId) { return true; }
                    bool isSuccAll = appMaster->IsSuccAll(true);
                    appMaster->onModuleReport(appModule, (appMaster->m_moduleCounter == 0 || appMaster->m_moduleCounter == appMaster->m_setModule.size()), isSuccAll);
                }
                else if (event->uEvtId == AppLoad::Event::Event_Core_AppExit_REQ)
                {
                    AppMaster* appMaster = AppLoad::GetInst()->getMaster();
                    for (auto& it : appMaster->m_setModule) 
                    {
                        it->onAppReqExit();
                    }
                    appMaster->m_alreadyExecuted = false;
                }

                bool isRunning = (event->uEvtId != AppLoad::Event::Event_Core_AppExit_REP);

                if (!event->isPost)
                {
                    m_signSend.Release();
                }

                return isRunning;
            }

        public:
            std::string strName;
            bool isLoading;
            bool isSucceed[2];

        private:
            bool isReadied;
            std::queue<Event*> m_queEvent;
            xCores::Signal m_signDeal;
            xCores::Signal m_signSend;
            xCores::Mutex m_mtxListener;
            std::map<uint32_t/*uEvtId*/, std::set<Listener*>> m_mapListener;
            uint32_t m_threadId;

            AppMaster* appMaster;
        };

        class AppMaster: public AppModule
        {
            friend class AppLoad;
            friend class Listener;
            friend class AppModule;

        public:
            AppMaster(const std::string& strName):
                AppModule(this, strName), m_alreadyExecuted(false), m_moduleCounter(0)
            { 
                AppLoad::GetInst()->m_pAppMaster = this;
                m_moduleCounter = 1;
                this->isLoading = true;
                this->isSucceed[0] = true;
                this->isSucceed[1] = true;
                this->m_threadId = Engine::GetCurId();
                m_setModule.insert(this);
            }
            virtual ~AppMaster() {}

            void Run()
            {
                this->m_alreadyExecuted = true;

                this->onAppReqInit();
                for (auto& it : m_setModule) 
                {
                    it->onAppReqInit();
                }
                this->onMasterInit();
                for (auto& it : m_setModule) 
                {
                    if (it == this) 
                    { 
                        // it->m_threadId = Engine::GetCurId();
                        continue; 
                    }
                    it->m_threadId = Engine::Ignite(it); // 线程内部调用onAppModuleInit
                    AppLoad::GetInst()->BindEvent(it, Event::Event_Core_AppExit_REQ);
                    AppLoad::GetInst()->BindEvent(it, Event::Event_Core_AppExit_REP);
                }
				AppLoad::GetInst()->BindEvent(this, Event::Event_Core_ModInit_REQ);
				AppLoad::GetInst()->BindEvent(this, Event::Event_Core_ModInit_REP);
				AppLoad::GetInst()->BindEvent(this, Event::Event_Core_ModExit_REQ);
				AppLoad::GetInst()->BindEvent(this, Event::Event_Core_ModExit_REP);
				AppLoad::GetInst()->BindEvent(this, Event::Event_Core_AppExit_REP);
				// 必须通过Event_CORE_APPEXIT_REQ请求主线程退出，进而出发各子线程退出事件，onMasterExec必须在所有onModuleExit执行结束后结束
				this->onMasterExec();
				AppLoad::GetInst()->UnBindEvent(this, Event::Event_Core_ModInit_REQ);
				AppLoad::GetInst()->UnBindEvent(this, Event::Event_Core_ModInit_REP);
				AppLoad::GetInst()->UnBindEvent(this, Event::Event_Core_ModExit_REQ);
				AppLoad::GetInst()->UnBindEvent(this, Event::Event_Core_ModExit_REP);
				AppLoad::GetInst()->UnBindEvent(this, Event::Event_Core_AppExit_REP);
                for (auto& it : m_setModule)
                {
                    if (it == this)
                    {
                        continue;
                    }
                    it->m_threadId = Engine::Ignite(it); // 线程内部调用onAppModuleInit
                    AppLoad::GetInst()->UnBindEvent(it, Event::Event_Core_AppExit_REQ);
                    AppLoad::GetInst()->UnBindEvent(it, Event::Event_Core_AppExit_REP);
                    Engine::Stall(it->m_threadId);
                }
				this->onMasterExit();
            }

            void Exit()
            {

            }

            bool IsSuccAll(bool load)
            {
                bool isSuccAll = true;
                for (auto& it : appMaster->m_setModule) { isSuccAll &= (load? it->isSucceed[0] : it->isSucceed[1]); }
                return isSuccAll;
            }

        protected:

            AppModule* getModule(uint32_t id)
            {
                for(auto& it : m_setModule)
                {
                    if (it->m_threadId == id)
                    {
                        return it;
                    }
                }
                return nullptr;
            }

            void regist(AppModule* appModule)
            {
                if (appModule != this)
                {
                    m_setModule.insert(appModule);
                }
            }

            void report(AppModule* appModule, bool isLoading)
            {
                appModule->isLoading = isLoading;
                Event event;
                event.userPtr = appModule;
                if (appModule->isLoading)
                {
                    ++m_moduleCounter;
                    event.uEvtId = Event::Event_Core_ModInit_REQ;
                    AppLoad::GetInst()->PostEvent(event);
                    appModule->isSucceed[0] = appModule->onModuleInit();
                    event.uEvtId = Event::Event_Core_ModInit_REP;
                }
                else
                {
                    event.uEvtId = Event::Event_Core_ModExit_REQ;
                    AppLoad::GetInst()->PostEvent(event);
                    appModule->isSucceed[1] = appModule->onModuleExit();
                    event.uEvtId = Event::Event_Core_ModExit_REP;
                    --m_moduleCounter;
                }

                // 用互斥锁保证当前AppModule与AppMaster同步
                AppLoad::GetInst()->PostEvent(event);

                if (!m_alreadyExecuted && m_moduleCounter == 0)
                {
                    AppLoad::Event event(AppLoad::Event::Event_Core_AppExit_REP);
                    AppLoad::GetInst()->PostEvent(event);
                }
            }

            virtual void onModuleReport(AppModule* appModule, bool isFinished, bool isSuccAll) {}
            
            virtual bool onMasterInit() { return onModuleInit(); }; // 此时所有onModuleInit均未开始执行，考虑删掉此函数
            virtual int onMasterExec() { return onModuleExec(); }
            virtual bool onMasterExit() { return onModuleExit(); } // 此时所有onModuleExit已经执行完成，考虑删掉此函数

            virtual void onEngineExec(uint32_t id, void* vData) override
            {
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
                AppModule* appModule = (AppModule*)vData;
                this->report(appModule, true);
                appModule->onModuleExec();
                this->report(appModule, false);
            }

            virtual void onEngineExit(uint32_t id, void* vData) override
            {
            }

        public:
            bool m_alreadyExecuted;
            std::atomic<uint8_t> m_moduleCounter;

        private:
            std::set<AppModule*> m_setModule; // 不加锁，因此Run之前必须初始化好
            
            virtual bool onModuleInit() { return true; };
            virtual int onModuleExec() { while(dispEvent(waitEvent())); return 0; }
            virtual bool onModuleExit() { return true; }
        };

        bool BindEvent(Listener* pListener, ...)
        {
            va_list body;
            va_start(body, pListener);
            uint32_t uEvtId = va_arg(body, uint32_t);
            pListener->m_pAppModule->m_mtxListener.Lock();
            do
            {
                if (uEvtId == Event::Event_Core_None) { break; }
                pListener->m_pAppModule->m_mapListener[uEvtId].insert(pListener);
                uEvtId = va_arg(body, uint32_t);
            } while (true);
            pListener->m_pAppModule->m_mtxListener.Unlock();
            va_end(body);
            return true;
        }

        bool UnBindEvent(Listener* pListener, ...)
        {
            va_list body;
            va_start(body, pListener);
            uint32_t uEvtId = va_arg(body, uint32_t);
            pListener->m_pAppModule->m_mtxListener.Lock();
            if (uEvtId == Event::Event_Core_None)
            {
                for (auto& it : pListener->m_pAppModule->m_mapListener)
                {
                    it.second.erase(pListener);
                }
            }
            else
            {
                do
                {
                    if (uEvtId == Event::Event_Core_None) { break; }
                    pListener->m_pAppModule->m_mapListener[uEvtId].erase(pListener);
                    uEvtId = va_arg(body, uint32_t);
                } while (true);
            }
            pListener->m_pAppModule->m_mtxListener.Unlock();
            va_end(body);

            return true;
        }
        
        void PostEvent(const Event& evt)
        {
            AppMaster* appMaster = m_pAppMaster;
            Event* event = new Event(evt);
            event->isPost = true;
            event->fromId = xCores::Engine::GetCurId();
            // AppModule* appModule = appMaster->getModule(event.fromId);
            // if (appModule != nullptr)
            // {
            //     appModule->hookPostEvent(event);
            // }
            bool isCoreEvent = (event->uEvtId > Event::Event_Core_Base && event->uEvtId < Event::Event_Core_Dead);
            for(auto& it: appMaster->m_setModule)
            {
                it->m_mtxListener.Lock();
                auto listeners = it->m_mapListener.find(event->uEvtId);
                if (/*(it->m_threadId == appMaster->m_threadId && isCoreEvent) || */(listeners != it->m_mapListener.end() && listeners->second.size() > 0))
                {
                    it->hookPostEvent(event);
                    it->m_queEvent.push(event);
                    it->m_signDeal.Release();
                }
                it->m_mtxListener.Unlock();
            }
        }

        const Event* SendEvent(const Event& evt)
        {
            AppMaster* appMaster = m_pAppMaster;
            Event* event = new Event(evt);
            event->isPost = false;
            event->fromId = xCores::Engine::GetCurId();
            // AppModule* appModule = appMaster->getModule(event.fromId);
            // if (appModule != nullptr)
            // {
            //     appModule->hookSendEvent(event);
            // }
            bool isCoreEvent = (event->uEvtId > Event::Event_Core_Base && event->uEvtId < Event::Event_Core_Dead);
            for(auto& it: appMaster->m_setModule)
            {
                it->m_mtxListener.Lock();
                auto listeners = it->m_mapListener.find(event->uEvtId);
                if ((it->m_threadId == appMaster->m_threadId && isCoreEvent) || (listeners != it->m_mapListener.end() && listeners->second.size() > 0))
                {
                    it->hookSendEvent(event);
                    it->m_queEvent.push(event);
                    it->m_signDeal.Release();
                    it->m_signSend.Acquire();
                }
                it->m_mtxListener.Unlock();
            }
            return event;
        }

        void SendEvent(Event* evt)
        {
            *evt = *SendEvent(*evt);
        }

        static void timerProc(uint32_t timerId, void* vData)
        {
            Event event(Event::Event_Core_AppTick_REP);
            event.tickId = timerId;
            event.userPtr = vData;
            AppLoad::GetInst()->PostEvent(event);
        }

        bool StartTimer(uint32_t timerId, uint32_t msPeriod, void* vData)
        {
            getMaster()->startTimer(timerId, msPeriod, AppLoad::timerProc, vData);
            return true;
        }
    
        bool StopTimer(uint32_t timerId)
        {
            getMaster()->stopTimer(timerId);
            return true;
        }

        void PostExit()
        {
            PostEvent(Event::Event_Core_AppExit_REQ);
            PostEvent(Event::Event_Core_AppExit_REP);
        }
    
    protected:
        AppMaster* getMaster()
        {
            return m_pAppMaster;
        }

    private:
        AppLoad(): m_pAppMaster(nullptr) {}
        virtual ~AppLoad() {}
        AppMaster* m_pAppMaster;

    };
}

#endif
